using System;
using System.Collections;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Threading;

namespace Controls
{
    public partial class AutoCompleteTextBox : Canvas, IComponentConnector
    {
        private delegate void TextChangedCallback();

        private VisualCollection controls;

        private TextBoxEx textBox = new TextBoxEx();

        private ComboBox comboBox = new ComboBox();

        private Timer keypressTimer = new Timer();

        private bool insertText;

        public static readonly DependencyProperty DelayTimeProperty = DependencyProperty.Register("DelayTime", typeof(int), typeof(AutoCompleteTextBox), new UIPropertyMetadata(0));

        public static readonly DependencyProperty ThresholdProperty = DependencyProperty.Register("Threshold", typeof(int), typeof(AutoCompleteTextBox), new UIPropertyMetadata(0));

        public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(AutoCompleteTextBox), new PropertyMetadata(null, OnTextChanged));

        public static readonly DependencyProperty CompletionListProperty = DependencyProperty.Register("CompletionList", typeof(IList), typeof(AutoCompleteTextBox), new UIPropertyMetadata(null));

        public int DelayTime
        {
            get
            {
                return (int)GetValue(DelayTimeProperty);
            }
            set
            {
                SetValue(DelayTimeProperty, value);
            }
        }

        public int Threshold
        {
            get
            {
                return (int)GetValue(ThresholdProperty);
            }
            set
            {
                SetValue(ThresholdProperty, value);
            }
        }

        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }

        public IList CompletionList
        {
            get
            {
                return (IList)GetValue(CompletionListProperty);
            }
            set
            {
                SetValue(CompletionListProperty, value);
            }
        }

        protected override int VisualChildrenCount
        {
            get
            {
                return controls.Count;
            }
        }

        private static void OnTextChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            AutoCompleteTextBox autoCompleteTextBox = sender as AutoCompleteTextBox;
            if (autoCompleteTextBox != null)
            {
                autoCompleteTextBox.insertText = true;
            }
        }

        public AutoCompleteTextBox()
        {
            controls = new VisualCollection(this);
            InitializeComponent();
            keypressTimer.Elapsed += OnTimedEvent;
            comboBox.IsSynchronizedWithCurrentItem = true;
            comboBox.IsTabStop = false;
            comboBox.SelectionChanged += comboBox_SelectionChanged;
            textBox.GotFocus += textBox_GotFocus;
            textBox.LostFocus += textBox_LostFocus;
            textBox.TextChanged += textBox_TextChanged;
            textBox.VerticalContentAlignment = VerticalAlignment.Center;
            controls.Add(comboBox);
            controls.Add(textBox);
        }

        private void textBox_LostFocus(object sender, RoutedEventArgs e)
        {
            comboBox.IsDropDownOpen = false;
        }

        private void textBox_GotFocus(object sender, RoutedEventArgs e)
        {
            TextChanged();
        }

        private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (comboBox.SelectedItem != null)
            {
                insertText = true;
                ComboBoxItem comboBoxItem = (ComboBoxItem)comboBox.SelectedItem;
                textBox.Text = comboBoxItem.Content.ToString();
            }
        }

        private void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            keypressTimer.Stop();
            base.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new TextChangedCallback(TextChanged));
        }

        private void textBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (insertText)
            {
                insertText = false;
            }
            else if (DelayTime > 0)
            {
                keypressTimer.Interval = DelayTime;
                keypressTimer.Start();
            }
            else
            {
                TextChanged();
            }
        }

        private void TextChanged()
        {
            if (CompletionList == null)
            {
                return;
            }
            try
            {
                comboBox.Items.Clear();
                if (textBox.Text.Length >= Threshold)
                {
                    foreach (string completion in CompletionList)
                    {
                        if (completion.StartsWith(textBox.Text, StringComparison.CurrentCultureIgnoreCase))
                        {
                            ComboBoxItem comboBoxItem = new ComboBoxItem();
                            comboBoxItem.Content = completion.ToString();
                            comboBox.Items.Add(comboBoxItem);
                        }
                    }
                    comboBox.IsDropDownOpen = comboBox.HasItems;
                }
                else
                {
                    comboBox.IsDropDownOpen = false;
                }
            }
            catch
            {
            }
        }

        protected override Size ArrangeOverride(Size arrangeSize)
        {
            textBox.Arrange(new Rect(arrangeSize));
            comboBox.Arrange(new Rect(arrangeSize));
            return base.ArrangeOverride(arrangeSize);
        }

        protected override Visual GetVisualChild(int index)
        {
            return controls[index];
        }
    }
}
